﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Sonce.Editor
{
	public partial class Layer : Canvas
	{
		#region Members
		private double width = 0;
		private double height = 0;
		private double gridRange = 10;
		private int majorGridLine = 5;
		private bool isFloating = false;
		private Brush strokeBrush = new SolidColorBrush(Colors.Green);
		private Brush fillBrush = new SolidColorBrush(Colors.Transparent);
		#endregion

		#region Properties
		/// <summary>
		/// Get or set the layer width.
		/// </summary>
		public new double Width
		{
			get
			{
				return (base.Width > 0 ? base.Width : 1200);
			}
			set
			{
				this.width = value;
				base.Width = value;
			}
		}

		/// <summary>
		/// Get or set the layer height.
		/// </summary>
		public new double Height
		{
			get
			{
				return (base.Height > 0 ? base.Height : 1200);
			}
			set
			{
				this.height = value;
				base.Height = value;
			}
		}

		/// <summary>
		/// If this property is set to true then all objects are automatically removed after mouse button is released.
		/// This way only one object can be drawn at the time.
		/// </summary>
		public bool IsFloating
		{
			get { return this.isFloating; }
			set { this.isFloating = value; }
		}

		/// <summary>
		/// Get or set the grid cell size in pixels.
		/// </summary>
		public double GridRange
		{
			get
			{
				return this.gridRange;
			}
			set
			{
				if (value > 0.0)
				{
					this.gridRange = value;
				}
			}
		}

		/// <summary>
		/// Defines which n-th grid lines are major.
		/// </summary>
		public int MajorGridLine
		{
			get
			{
				return this.majorGridLine;
			}
			set
			{
				this.majorGridLine = value;
			}
		}
		#endregion

		#region Constructor
		/// <summary>
		/// Initializes a new Layer instance.
		/// </summary>
		public Layer()
		{
		}
		#endregion

		#region Methods
		/// <summary>
		/// Remove all visual objects from the current layer.
		/// </summary>
		public void Clear()
		{
			this.Children.Clear();
		}
		#endregion

		#region Events
		/// <summary>
		/// Occurs when an visual object is drawn on the layer.
		/// </summary>
		public event EventHandler<LayerEventArgs> VisualObjectDrawn;
		protected virtual void OnVisualObjectDrawn(LayerEventArgs e)
		{
			if (VisualObjectDrawn != null)
			{
				VisualObjectDrawn(this, e);
			}
		}
		#endregion

		#region Dependency properties
		public Brush StrokeBrush
		{
			get { return (Brush)GetValue(StrokeBrushProperty); }
			set { SetValue(StrokeBrushProperty, value); }
		}

		public static readonly DependencyProperty StrokeBrushProperty = DependencyProperty.Register("StrokeBrush", typeof(Brush), typeof(Layer), new PropertyMetadata(StrokeBrushChanged));

		private static void StrokeBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
		}

		//Get or set the default background brush.
		//This is NOT layer background, it is actually fill brush for the visual object in this layer.
		public Brush FillBrush
		{
			get { return (Brush)GetValue(FillBrushProperty); }
			set { SetValue(FillBrushProperty, value); }
		}

		public static readonly DependencyProperty FillBrushProperty = DependencyProperty.Register("FillBrush", typeof(Brush), typeof(Layer), new PropertyMetadata(FillBrushChanged));

		private static void FillBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
		}
		#endregion

		#region Grid, cursor
		/// <summary>
		/// Overloaded. Draw a grid on the whole layer.
		/// </summary>
		public void DrawGrid()
		{
			DrawGrid(this.GridRange, this.MajorGridLine, 0.12, 0.20);
		}

		/// <summary>
		/// Draw a grid on the whole layer.
		/// </summary>
		/// <param name="gridRange">Grid cell size in pixels.</param>
		/// <param name="majorGridLine">Defines which n-th grid lines are major.</param>
		/// <param name="minorOpacity">Minor line opacity (values from 0 to 1).</param>
		/// <param name="majorOpacity">Major line opacity (values from 0 to 1).</param>
		public void DrawGrid(double gridRange, int majorGridLine, double minorOpacity, double majorOpacity)
		{
			int max;
			Path path;

			//Horizontal lines
			max = (int)(this.Width / gridRange) + 1;
			for (int i = 0; i < max; i++)
			{
				Point point1 = new Point(0, i * gridRange);
				Point point2 = new Point(this.Width, i * gridRange);

				//Draw line
				string data = string.Format("M{0},{1} L{2},{3}", point1.X, point1.Y, point2.X, point2.Y);
				path = Helper.CreatePath(data);
				path.Stroke = this.StrokeBrush;
				path.StrokeThickness = 1.0;
				path.HorizontalAlignment = HorizontalAlignment.Left;
				path.VerticalAlignment = VerticalAlignment.Top;
				Draw(path);

				if (majorGridLine > 0)
				{
					path.Opacity = (i % majorGridLine != 0 ? minorOpacity : majorOpacity);
				}
				else
				{
					path.Opacity = majorOpacity;
				}
			}

			//Vertical lines
			max = (int)(this.Height / gridRange) + 1;
			for (int i = 0; i < max; i++)
			{
				Point point1 = new Point(i * gridRange, 0);
				Point point2 = new Point(i * gridRange, this.Height);

				//Draw line
				string data = string.Format("M{0},{1} L{2},{3}", point1.X, point1.Y, point2.X, point2.Y);
				path = Helper.CreatePath(data);
				path.Stroke = this.StrokeBrush;
				path.StrokeThickness = 1.0;
				path.HorizontalAlignment = HorizontalAlignment.Left;
				path.VerticalAlignment = VerticalAlignment.Top;
				Draw(path);

				if (majorGridLine > 0)
				{
					path.Opacity = (i % majorGridLine != 0 ? minorOpacity : majorOpacity);
				}
				else
				{
					path.Opacity = majorOpacity;
				}
			}
		}

		/// <summary>
		/// Draw a small dot at the specified location.
		/// </summary>
		/// <param name="position">Location of the dot.</param>
		/// <returns>Returns the drawn visual object for futher customization.</returns>
		public Ellipse DrawPoint(Point position)
		{
			Ellipse ellipse = new Ellipse();
			ellipse.Width = 2;
			ellipse.Height = 2;
			ellipse.Margin = new Thickness(position.X - 1, position.Y - 1, 0, 0);
			ellipse.Stroke = this.StrokeBrush;

			if (this.isFloating)
			{
				this.Children.Clear();
			}
			this.Children.Add(ellipse);

			return ellipse;
		}

		/// <summary>
		/// Draw a cursor at the specified location.
		/// </summary>
		/// <param name="position">Location of the dot.</param>
		/// <returns>Returns the drawn visual object for futher customization.</returns>
		public Path DrawCursor(Point position)
		{
			Path path = Helper.CreatePath("M-5,0 L5,0 M0,-5 L0,5");
			path.Margin = new Thickness(position.X, position.Y, 0, 0);
			path.Stroke = new SolidColorBrush(Colors.Yellow);
			path.StrokeThickness = 1.0;

			if (this.isFloating)
			{
				this.Children.Clear();
			}
			this.Children.Add(path);

			return path;
		}

		//DrawCursor from resources
		public FrameworkElement DrawCursor(Point position, FrameworkElement cursor)
		{
			double offsetX = cursor.Width / 2;
			double offsetY = cursor.Height / 2;
			cursor.Margin = new Thickness(position.X - offsetX, position.Y - offsetY, 0, 0);

			//Override cursor color
			if (cursor is Path)
			{
				((Path)cursor).Stroke = new SolidColorBrush(Colors.Yellow);
			}

			if (this.isFloating)
			{
				this.Children.Clear();
			}
			this.Children.Add(cursor);

			return cursor;
		}
		#endregion

		#region Drawing
		public void Draw(FrameworkElement element)
		{
			if (this.isFloating)
			{
				this.Children.Clear();
			}
			this.Children.Add(element);

			OnVisualObjectDrawn(new LayerEventArgs(element));
		}

		public void Draw(FrameworkElement element, Point position)
		{
			element.Margin = new Thickness(position.X, position.Y, 0, 0);
			element.HorizontalAlignment = HorizontalAlignment.Left;
			element.VerticalAlignment = VerticalAlignment.Top;

			if (this.isFloating)
			{
				this.Children.Clear();
			}
			this.Children.Add(element);

			OnVisualObjectDrawn(new LayerEventArgs(element, position));
		}
		#endregion
	}
}
